This example mirrors tracts2.html.

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images"
DATA_FOLDER = Path("images")

# Download data for example
download_dataset(
    BASE_API_URL,
    DATA_FOLDER,
    files=[
        "mni152.nii.gz",
        "tract.FAT_R.vtk",
        "tract.IFOF_R.trk",
        "tract.SLF1_R.tck",
        "BrainMesh_ICBM152.lh.mz3",
    ],
)
mni152.nii.gz already exists.
tract.FAT_R.vtk already exists.
tract.IFOF_R.trk already exists.
tract.SLF1_R.tck already exists.
BrainMesh_ICBM152.lh.mz3 already exists.
Dataset downloaded successfully to images.
In [2]:
import ipywidgets as widgets
from IPython.display import display

import ipyniivue

# 1. initialize NiiVue
nv = ipyniivue.NiiVue()
nv.opts.show_3d_crosshair = True
nv.opts.back_color = [0, 0, 0, 1]
nv.opts.is_orient_cube = True
nv.opts.slice_type = ipyniivue.SliceType.RENDER

# 2. load volume
nv.add_volume({"path": DATA_FOLDER / "mni152.nii.gz"})

# 3. load meshes
meshes = [
    # Tract 0
    {"path": DATA_FOLDER / "tract.FAT_R.vtk", "rgba255": [180, 180, 0, 255]},
    # Tract 1
    {"path": DATA_FOLDER / "tract.IFOF_R.trk", "rgba255": [0, 255, 0, 255]},
    # Tract 2
    {"path": DATA_FOLDER / "tract.SLF1_R.tck", "rgba255": [0, 0, 255, 255]},
    # Brain Mesh (Index 3)
    {
        "path": DATA_FOLDER / "BrainMesh_ICBM152.lh.mz3",
        "rgba255": [242, 174, 177, 255],
        "opacity": 0.2,
    },
]
nv.load_meshes(meshes)

# 4. set initial scene props
nv.set_clip_plane(-0.1, 0, 90)
nv.set_render_azimuth_elevation(135, 15)

# 5. create ui widgets
shader_drop = widgets.Dropdown(
    options=["Edge", "Flat", "Matcap", "Matte", "Outline", "Phong", "Toon"],
    value="Outline",
    description="Shader:",
)

color_drop = widgets.Dropdown(
    options=[
        ("Global direction", "Global"),
        ("Local direction", "Local"),
        ("Fixed", "Fixed"),
    ],
    value="Fixed",
    description="Color:",
)

fiber_radius = widgets.FloatSlider(
    min=0, max=2.0, step=0.1, value=0, description="Radius"
)

xray_slider = widgets.FloatSlider(
    min=0, max=0.2, step=0.01, value=0.1, description="MeshXRay"
)

zoom_slider = widgets.FloatSlider(
    min=0.1, max=2.0, step=0.1, value=1.0, description="Zoom"
)


# 6. define callbacks
def on_shader_change(change):
    """Apply shader to the brain mesh (index 3)."""
    if len(nv.meshes) > 3:
        nv.set_mesh_shader(nv.meshes[3].id, change["new"])


def on_color_change(change):
    """Apply fiber color to the tracts (indices 0, 1, 2)."""
    val = change["new"]
    for i in range(3):
        if i < len(nv.meshes):
            nv.meshes[i].fiber_color = val


def on_radius_change(change):
    """Apply radius to the tracts (indices 0, 1, 2)."""
    val = change["new"]
    for i in range(3):
        if i < len(nv.meshes):
            nv.meshes[i].fiber_radius = val


def on_xray_change(change):
    """Handle xray change."""
    nv.opts.mesh_xray = change["new"]


def on_zoom_change(change):
    """Handle zoom change."""
    nv.scene.vol_scale_multiplier = change["new"]


# 7. Bind Callbacks
shader_drop.observe(on_shader_change, names="value")
color_drop.observe(on_color_change, names="value")
fiber_radius.observe(on_radius_change, names="value")
xray_slider.observe(on_xray_change, names="value")
zoom_slider.observe(on_zoom_change, names="value")

# 8. Set Initial Values
nv.opts.mesh_xray = xray_slider.value

for i in range(3):
    nv.meshes[i].fiber_color = color_drop.value
    nv.meshes[i].fiber_radius = fiber_radius.value

nv.set_mesh_shader(nv.meshes[3].id, shader_drop.value)


# 9. display all
ui = widgets.VBox(
    [
        widgets.HBox([shader_drop, color_drop]),
        widgets.HBox([fiber_radius, xray_slider, zoom_slider]),
        nv,
    ]
)

display(ui)